#include "kernel.h"
// Somewhere at the top of network.c, where it's easily visible:
#define RING_SIZE 16
#define BUFFER_SIZE 4000
#define PACKET_LENGTH 9

volatile struct dev_net *dev_net;
struct dma_ring_slot* ring;
struct honeypot_command_packet* commandloc;
int* queue_lock;
int* ipHash_lock;
int* destHash_lock;
int* fingerHash_lock;
struct fingerprint_node* k;
struct ipSrc_node* h;
struct destPort_node* g;



void network_init(){

	/*find out where I/O region for the network card is*/
	for(int i = 0; i < 16; i++){
		if (bootparams->devtable[i].type == DEV_TYPE_NETWORK){
			printf_m("Detected network device... \n");
			//find the vitual address that maps to this I/O region
			dev_net = physical_to_virtual(bootparams->devtable[i].start);
			
		}
	}
	
	// initlize the ring buffer
	ring = (struct dma_ring_slot*) malloc(sizeof(struct dma_ring_slot) * RING_SIZE);
	
	dev_net->rx_base = virtual_to_physical(ring);// initialize the ring in the ring buffer
	dev_net->rx_capacity = RING_SIZE; // set the size of the ring
	
	dev_net->rx_head = virtual_to_physical(ring); // add buffer
	dev_net->rx_tail = virtual_to_physical(ring); // the buffer is empty so rx_head = rx_tail
	printf_m("Ring created \n");
	
	for (int i = 0; i < RING_SIZE; ++i) {
		// create the space for the ring buffer
		void* space = malloc(BUFFER_SIZE);
		printf_m("created ring buffer %d at %x \n", i, (int) space);
		ring[i].dma_base = virtual_to_physical(space);
		ring[i].dma_len = BUFFER_SIZE;
	}
	// Create mutex lock for the queue
	queue_lock = malloc(sizeof(int));
	*queue_lock = 0;
	
	ipHash_lock = malloc(sizeof(int));
	*ipHash_lock = 0;
	
	destHash_lock = malloc(sizeof(int));
	*destHash_lock = 0;
	
	fingerHash_lock = malloc(sizeof(int));
	*fingerHash_lock = 0;
	
	k=malloc(HASHTABLE_SIZE*sizeof(struct fingerprint_node));
	h=malloc(HASHTABLE_SIZE*sizeof(struct ipSrc_node));
	g=malloc(HASHTABLE_SIZE*sizeof(struct destPort_node));
	hashtable_init(k,h,g);

	printf_m("ring buffer created too \n");
	
}

void network_start_receive(){
  // start processing data from the card
  dev_net->cmd = NET_SET_RECEIVE;
  dev_net->data = 1;
  
  dev_net->cmd = NET_SET_POWER;
  dev_net->data = 1;
  printf_m("Packets now being sent \n");
}

void network_set_interrupts(int opt){
	if(opt==1){
		set_cpu_status(current_cpu_status() | (1 << (8+INTR_NETWORK)));
	}else{
		 set_cpu_status(current_cpu_status() | (0 << (8+INTR_NETWORK)));
	}

}

void network_trap(){
	while(1){
		//handle_packet();
	}

}

void network_poll(struct queue_str* firstbuff){
	while(1){
		handle_packet(firstbuff);
	}
}

void handle_packet(struct queue_str* firstbuff){

	if(dev_net->rx_tail != dev_net->rx_head){
		
		// get the address for the next packet to read
		unsigned int ring_num = (dev_net->rx_tail % dev_net->rx_capacity);
		unsigned int dma_len = ring[ring_num].dma_len;
		
		// get the physical address of the ring in memory
		commandloc = (struct honeypot_command_packet*) ring[ring_num].dma_base;
		
		// convert to a virtual address so we can reference it
		commandloc = (struct honeypot_command_packet*) physical_to_virtual((int *)commandloc);
		
		// put the packet in the queue
		enqueue_lock_packet(commandloc, firstbuff, dma_len, queue_lock);
		
		//zero out the memory
		commandloc->headers.ip_version = (char) 0;
		commandloc->headers.ip_qos = 0;
		commandloc->headers.ip_len = 0;
		commandloc->headers.ip_id = 0;
		commandloc->headers.ip_flags = 0;
		commandloc->headers.ip_ttl = 0;
		commandloc->headers.ip_protocol = 0;
		commandloc->headers.ip_checksum = 0;
		commandloc->headers.ip_source_address_big_endian = 0;
		commandloc->headers.ip_dest_address_big_endian = 0;
		commandloc->headers.udp_source_port_big_endian= 0;
		commandloc->headers.udp_dest_port_big_endian = 0;
		commandloc->headers.udp_len = 0;
		commandloc->headers.udp_checksum = 0;
		commandloc->secret_big_endian = 0;
		commandloc->cmd_big_endian = 0;
		commandloc->data_big_endian = 0;
		
		char* data_ref = (char *) &(commandloc->data);	
		int data_size = dma_len - sizeof(commandloc);
		
		for(int i = 0; i < data_size; i++){
			data_ref[i] = (char) 0;
		}
		
	(dev_net->rx_tail)++;
	}
}

void handle_cmd(struct node_packet *node){
	printf_m("gettting %x, comparing to %x\n",node->pkt.secret_big_endian, HONEYPOT_SECRET_BIG_ENDIAN);
	if(node->pkt.secret_big_endian == HONEYPOT_SECRET_BIG_ENDIAN){
		
		printf_m("This is a command packet %x \n", node->pkt.cmd_big_endian);
		
		if(node->pkt.cmd_big_endian == HONEYPOT_ADD_SPAMMER_BIG_ENDIAN){
			// handle addint a spammer
			printf_m("adding a spammer \n");
			addIpSrc(h,node,ipHash_lock);
			
		}else if(node->pkt.cmd_big_endian == HONEYPOT_ADD_EVIL_BIG_ENDIAN){
			// handle adding an evil packet hash
			printf_m("adding an evil hash\n");
			addFingerprint(k,node,fingerHash_lock);
			
		}else if(node->pkt.cmd_big_endian == HONEYPOT_ADD_VULNERABLE_BIG_ENDIAN){
			// handle addint a vunlerable port
			printf_m("adding a vulnerable hash\n");
			addDestPort(g,node,destHash_lock);
			
		}else if(node->pkt.cmd_big_endian == HONEYPOT_DEL_SPAMMER_BIG_ENDIAN){
			// handle removing a spammer
			printf_m("removing a spammer\n");
			removeIpSrc(h,node,ipHash_lock);
			
		}else if(node->pkt.cmd_big_endian == HONEYPOT_DEL_EVIL_BIG_ENDIAN ){
			//handle removing an evil packet
			printf_m("removing an evil hash\n");
			removeFingerprint(k,node,fingerHash_lock);
			
		}else if(node->pkt.cmd_big_endian == HONEYPOT_DEL_VULNERABLE_BIG_ENDIAN ){
			//handle removing a vulnurable port
			printf_m("removing a vulnerable port\n");
			removeDestPort(g,node,destHash_lock);
			
		}else if(node->pkt.cmd_big_endian == HONEYPOT_PRINT_BIG_ENDIAN){
			printf_m("handling print packet \n");
			print_stats(k,h,g,fingerHash_lock,ipHash_lock,destHash_lock);
			free(node);
			//handle printing from the packet
		}else{
			printf_m("there is a problem with the data feild check \m");
		}
	
	}else{
		// the packet is not anything
		//check if it is in any of the lists then increment the number of packets processed
		printf_m("contains a fingerprint %d \n",containsFingerprint(k,node,fingerHash_lock));
		printf_m("contains a spammer ip %d \n",containsIpSrc(h,node,ipHash_lock));
		printf_m("cotains a destination port %d \n",containsDestPort(g,node,destHash_lock));
		free(node);
		inc_processed();

	}
	
}

void print_stat(){
	print_stats(k,h,g,fingerHash_lock,ipHash_lock,destHash_lock);
}

